#include "FileIO.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <map>
#include <filesystem>
#include <optional>     // ����ͷ�ļ�
#include <stdexcept>    // ����ͷ�ļ� for std::invalid_argument

namespace FileIO {

    // ��������������ɾ���ַ������˵Ŀհ��ַ� (trim)
    std::string trim(const std::string& s) {
        size_t first = s.find_first_not_of(" \t\n\v\f\r");
        if (std::string::npos == first) {
            return ""; // ���ȫ�ǿո񣬷��ؿ��ַ���
        }
        size_t last = s.find_last_not_of(" \t\n\v\f\r");
        return s.substr(first, (last - first + 1));
    }

    // ������������ȫ�ؽ��ַ���ת��Ϊ��������
    template<typename T>
    std::optional<T> safe_string_to_num(const std::string& s) {
        std::string trimmed_s = trim(s);
        if (trimmed_s.empty()) {
            return std::nullopt;
        }
        try {
            if constexpr (std::is_same_v<T, long long>) {
                return std::stoll(trimmed_s);
            }
            else if constexpr (std::is_same_v<T, int>) {
                return std::stoi(trimmed_s);
            }
        }
        catch (const std::invalid_argument&) {
            return std::nullopt; // �ַ���������Ч������
        }
        catch (const std::out_of_range&) {
            return std::nullopt; // ���ֳ�����Χ
        }
        return std::nullopt; // ��֧�ֵ�����
    }


    bool loadGraph(const std::string& nodes_path, const std::string& edges_path, Graph& graph, CodeMap& code_map) {
        // --- 1. ���غͽ����ڵ��ļ� (Nodes.csv) ---
        std::ifstream nodes_file(nodes_path);
        if (!nodes_file.is_open()) {
            std::cerr << "Error: Cannot open nodes file: " << nodes_path << std::endl;
            return false;
        }
        std::cout << "Starting to parse nodes file: " << nodes_path << std::endl;

        std::string line;
        std::getline(nodes_file, line); // Skip header

        std::unordered_map<std::string, int> op_map;
        std::unordered_map<std::string, int> type_map;
        int op_counter = 1, type_counter = 1;
        int max_id = 0;
        std::vector<NodeData> raw_nodes;
        int line_num = 1;

        while (std::getline(nodes_file, line)) {
            line_num++;
            if (line.empty() || line.find_first_not_of(" \t\n\v\f\r") == std::string::npos) {
                continue; // ��������
            }

            std::stringstream ss(line);
            std::string field;
            std::vector<std::string> fields;
            while (std::getline(ss, field, ',')) {
                fields.push_back(field);
            }

            if (fields.size() < 7) {
                std::cerr << "Warning: Line " << line_num << " in nodes file is malformed (has " << fields.size() << " columns, expected at least 7), skipping." << std::endl;
                continue;
            }

            NodeData current_node;

            // ID (mandatory)
            auto id_opt = safe_string_to_num<int>(fields[0]);
            if (!id_opt) {
                std::cerr << "Warning: Line " << line_num << " has invalid ID '" << fields[0] << "', skipping." << std::endl;
                continue;
            }
            current_node.id = id_opt.value() + 1;
            max_id = std::max(max_id, current_node.id);

            // Text fields
            current_node.op = trim(fields[1]);
            current_node.type = trim(fields[4]);
            current_node.pipe = trim(fields[5]);

            // Optional numeric fields
            current_node.buf_id = safe_string_to_num<int>(fields[2]);
            current_node.size = safe_string_to_num<long long>(fields[3]);
            current_node.cycles = safe_string_to_num<int>(fields[6]);

            // Bufs list
            if (fields.size() >= 8) {
                std::string bufs_str = trim(fields[7]);
                if (!bufs_str.empty()) {
                    std::replace(bufs_str.begin(), bufs_str.end(), ',', ' ');
                    std::stringstream bufs_ss(bufs_str);
                    int buf_val;
                    while (bufs_ss >> buf_val) {
                        current_node.bufs.push_back(buf_val);
                    }
                }
            }

            raw_nodes.push_back(current_node);

            if (!current_node.op.empty() && op_map.find(current_node.op) == op_map.end()) {
                op_map[current_node.op] = op_counter++;
            }
            if (!current_node.type.empty() && type_map.find(current_node.type) == type_map.end()) {
                type_map[current_node.type] = type_counter++;
            }
        }
        nodes_file.close();
        std::cout << "Nodes file parsed. " << raw_nodes.size() << " raw records." << std::endl;

        // --- 2. ��� code_map �����յ� nodes �ṹ������ ---
        if (op_map.count("ALLOC")) code_map.ALLOC = op_map["ALLOC"];
        if (op_map.count("FREE")) code_map.FREE = op_map["FREE"];
        if (type_map.count("L1")) code_map.L1 = type_map["L1"];
        if (type_map.count("UB")) code_map.UB = type_map["UB"];
        if (type_map.count("L0A")) code_map.L0A = type_map["L0A"];
        if (type_map.count("L0B")) code_map.L0B = type_map["L0B"];
        if (type_map.count("L0C")) code_map.L0C = type_map["L0C"];

        graph.num_nodes = max_id;
        graph.nodes.resize(max_id + 1);
        for (auto& raw_node : raw_nodes) {
            if (op_map.count(raw_node.op)) raw_node.op_code = op_map[raw_node.op];
            if (type_map.count(raw_node.type)) raw_node.type_code = type_map[raw_node.type];
            if (raw_node.id < graph.nodes.size()) {
                graph.nodes[raw_node.id] = raw_node;
            }
        }

        // --- 3. ���غͽ������ļ� (Edges.csv) ---
        std::ifstream edges_file(edges_path);
        if (!edges_file.is_open()) {
            std::cerr << "Error: Cannot open edges file: " << edges_path << std::endl;
            return false;
        }
        std::cout << "Starting to parse edges file: " << edges_path << std::endl;

        graph.adj_list.assign(graph.num_nodes + 1, {});
        graph.predecessors_list.assign(graph.num_nodes + 1, {});
        std::getline(edges_file, line); // Skip header if it exists
        line_num = 1;

        while (std::getline(edges_file, line)) {
            line_num++;
            if (line.empty() || line.find_first_not_of(" \t\n\v\f\r") == std::string::npos) {
                continue; // ��������
            }

            std::stringstream ss(line);
            std::string start_str, end_str;

            if (!std::getline(ss, start_str, ',') || !std::getline(ss, end_str, ',')) {
                std::cerr << "Warning: Line " << line_num << " in edges file is malformed, skipping." << std::endl;
                continue;
            }

            auto start_opt = safe_string_to_num<int>(start_str);
            auto end_opt = safe_string_to_num<int>(end_str);

            if (!start_opt || !end_opt) {
                std::cerr << "Warning: Line " << line_num << " in edges file has invalid node ID, skipping." << std::endl;
                continue;
            }

            int start_node = start_opt.value() + 1;
            int end_node = end_opt.value() + 1;

            if (start_node > 0 && start_node <= graph.num_nodes && end_node > 0 && end_node <= graph.num_nodes) {
                graph.adj_list[start_node].push_back(end_node);
                graph.predecessors_list[end_node].push_back(start_node);
            }
            else {
                std::cerr << "Warning: Line " << line_num << " in edges file has out-of-bounds node ID, skipping." << std::endl;
            }
        }
        edges_file.close();
        std::cout << "Edges file parsed." << std::endl;
        return true;
    }

    std::vector<int> loadScheduleFromFile(const std::string& filename) {
        std::ifstream file(filename);
        if (!file.is_open()) {
            throw std::runtime_error("Error: Cannot open schedule file: " + filename);
        }
        std::vector<int> schedule;
        int id;
        while (file >> id) {
            schedule.push_back(id + 1);
        }
        return schedule;
    }

    std::vector<std::pair<int, long long>> formatSpillLogForOutput(
        const std::unordered_map<int, long long>& spill_map, const Graph& graph) {

        std::vector<std::pair<int, long long>> spill_output_log;
        if (spill_map.empty()) return spill_output_log;

        std::vector<int> node_ids;
        for (const auto& pair : spill_map) {
            node_ids.push_back(pair.first);
        }
        std::sort(node_ids.begin(), node_ids.end());

        for (int node_id : node_ids) {
            long long address = spill_map.at(node_id);
            if (node_id < graph.nodes.size()) {
                const auto& node = graph.nodes.at(node_id);
                if (node.buf_id.has_value()) {
                    spill_output_log.push_back({ node.buf_id.value(), address });
                }
            }
        }
        return spill_output_log;
    }

} // namespace FileIO